package net.crazyadam

/**
 *
 * @author joseph
 * @since 6/15/12 5:22 PM
 */

import math._

class Earth {
  val radiusEquatorial = 6378.1370*1000
  //meters
  val radiusPolar = 6356.7523*1000
  //meters
  private val a = radiusEquatorial
  private val b = radiusPolar
  /**
   * radians,not degrees
   */
  var originLatitude = 0.0
  /**
   * radians,not degrees
   */
  var originLongitude = 0.0
  //
  var radiusMeridional = radiusEquatorial

  /**
   * @see <a href="https://en.wikipedia.org/wiki/Earth_radius#Meridional_Earth_radius">Radius at a given geodetic latitude</a>
   * @param latitude degrees,not radians
   * @return radius at the latitude
   */
  def radius(latitude: Double): Double = {
    val theta = toRadians(latitude)
    val m = radius(pow(a, 2) * cos(theta), pow(b, 2) * sin(theta))
    val n = radius(a * cos(theta), b * sin(theta))
    m / n

  }

  def radius(p: Point): Double = radius(p.latitude)

  /**
   * @param x of point (x, y) in Cartesian coordinates.
   * @param y of point (x, y) in Cartesian coordinates.
   * @return the radius component of the point (r, theta) in polar coordinates that corresponds to the point (x, y) in Cartesian coordinates.
   */
  private def radius(x: Double, y: Double): Double = {
    sqrt(pow(x, 2) + pow(y, 2))
  }

  /**
   * @see  Earth#radiuse
   * @return
   */
  def distance(x: Double, y: Double) = radius(x, y)

  /**
   *
   * @param p1 point at the start
   * @param p2 point at the end
   * @return  distance(meters) of the two points
   */
  def distance(p1: Point, p2: Point): Double = {
    /**
     * MUST convert degrees to radians
     *@see <a href="https://en.wikipedia.org/wiki/Haversine_formula">Haversine Formula</a>
     */
    2* radiusMeridional * asin(sqrt(pow(sin((toRadians(p2.latitude-p1.latitude))/2),2)+
      cos(toRadians(p1.latitude))*cos(toRadians(p2.latitude))*
      pow(sin((toRadians(p2.longitude-p1.longitude))/2),2)))
  }

  /**
   *
   * @param p1 point of start
   * @param p2 point of end
   * @return radians of the two points in polar coordinate,where W->E is the x axis and S->N is the y axis in  Cartesian coordinate
   */
  def theta(p1: Point, p2: Point): Double = {
    //atan2(y,x)
    atan2(p2.latitude - p1.latitude, (p2.longitude - p1.longitude) * cos(originLatitude))
  }

  /**
   *
   * @param longitude degrees,not radians
   * @param latitude degrees,not radians
   */
  def setOrigin(longitude: Double, latitude: Double) {
    originLongitude = toRadians(longitude)
    originLatitude = toRadians(latitude)
    radiusMeridional = radius(latitude)
  }

  def setOrigin(p: Point) {
    setOrigin(p.longitude, p.latitude)
  }

  def coordinateCartesian(longitude: Double, latitude: Double) = {
    val x = radiusMeridional * (toRadians(longitude) - originLongitude)
    val y = radiusMeridional * (toRadians(latitude) - originLatitude)
    x -> y
  }

}
